package br.edu.ufcg.splab.techniques.reduction.heuristics;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import br.edu.ufcg.splab.generator.TestCase;
import br.edu.ufcg.splab.generator.TestSuite;
import br.edu.ufcg.splab.techniques.reduction.InterfaceReduction;
import br.edu.ufcg.splab.techniques.reduction.requirement.InterfaceRequirement;
import br.edu.ufcg.splab.techniques.reduction.requirement.RequirementTracer;

public class Greedy implements InterfaceReduction {
	protected TestSuite testSuites;

	/**
	 * While not all test requirements are satisfied, the test case <i>t</i> that
	 * satisfies the maximum number  of unsatisfied test requirements is selected by
	 * the Greedy heuristic. If there is a tie situation,  a random choice is made. 
	 * This test case <i>t</i> is added  to Reduced Test Suite (<i>RS</i>) and 
	 * all test requirements that can be satisfied by that test
	 * case are marked as a satisfied test requirement.
	 * 
	 * @param suite The complete test suite.
	 * @param tracers The ordered list of requirements of the complete test suite.
	 * @return The reduced test suite.
	 */
	public TestSuite reduceTestSuite(TestSuite suite, List<RequirementTracer> tracers) {			
		
		List<TestCase> verifiedTests = new ArrayList<TestCase>(); 
		List<TestCase> reducedTests = new ArrayList<TestCase>();
		List<TestCase> toVerify = new ArrayList<TestCase>(suite.getTestSuite());

		for(RequirementTracer tracer: tracers){
	
			reducedTests.addAll(verifiedTests);
						
			toVerify = new ArrayList<TestCase>(suite.getTestSuite());
			toVerify.removeAll(reducedTests);
			
			verifiedTests = new ArrayList<TestCase>();		
			
			List<InterfaceRequirement> requirements = tracer.getRequirements();
			for(TestCase tc: reducedTests){
				requirements.removeAll(tracer.getRequirementsSatisfied(tc));
			}
			List<InterfaceRequirement> notCovered = new ArrayList<InterfaceRequirement>(requirements);
					
			do{
				TestCase toGet = this.getTheMostCovererOfNotSatisfied(toVerify, notCovered, tracer);
				List<InterfaceRequirement> covered = tracer.getRequirementsSatisfied(toGet);
				notCovered.removeAll(covered);
				toVerify.remove(toGet);
				verifiedTests.add(toGet);
			}while(notCovered.size() > 0);
		}
		
		reducedTests.addAll(verifiedTests);
		
		TestSuite newTS = new TestSuite();
		newTS.addAll(reducedTests);
		
		return newTS;
	}

	protected TestCase getTheMostCovererOfNotSatisfied(List<TestCase> toVerify, List<InterfaceRequirement> notCovered, RequirementTracer tracer){
		
		int maxNumberOfReqs = 0;
		List<TestCase> chosen = new ArrayList<TestCase>();
		for (TestCase testCase : toVerify) {
			int numberOfReqs = tracer.getRequirementsSatisfied(testCase, notCovered).size(); 
			if(numberOfReqs > maxNumberOfReqs)	{
				maxNumberOfReqs = numberOfReqs;
				chosen.clear();
			}
			if(numberOfReqs == maxNumberOfReqs)	{
				chosen.add(testCase);
			}
		}
		if(chosen.size() == 1){//if only one test case was chosen		
		    return chosen.get(0); 
		}else{ // else, a random choice is done.
		    return chosen.get(new Random().nextInt(chosen.size()));
		}		
	}
	
	protected TestCase getTheMostCoverer(List<TestCase> toVerify, RequirementTracer tracer){
		//System.out.println("toVerify: " + toVerify.size());
		int maxNumberOfReqs = 0;
		List<TestCase> chosen = new ArrayList<TestCase>();
		for (TestCase testCase : toVerify) {
			int numberOfReqs = tracer.getRequirementsSatisfied(testCase).size(); 
			if(numberOfReqs > maxNumberOfReqs)	{
				maxNumberOfReqs = numberOfReqs;
				chosen.clear();
			}
			if(numberOfReqs == maxNumberOfReqs)	{
				chosen.add(testCase);
			}
		}
		//System.out.println("chosen: " + chosen.size());
		if(chosen.size() == 1){//if only one test case was chosen		
		    return chosen.get(0); 
		}else{ // else, a random choice is done.
		    return chosen.get(new Random().nextInt(chosen.size()));
		}		
	}
	
	/**
     * Provide a string representation of the technique to be written.
     */
	public String toString() {
        return "G";
    }
	
//	public static void main(String[] args)
//	{
//		
//		
//			
//			
//		try{		
//			String tgf = "./test-data/reduction/graph_test_reduction.tgf";
//			
//			ReadTGF parser = new ReadTGF();			
//			InterfaceGraph graph = parser.getGraph(tgf);
//			
//			InterfaceGenerate generator = new TestCaseGenerator();
//			ConvertGraphToTree convertsGraphToTree = new ConvertGraphToTree();
//			Tree tree = convertsGraphToTree.converterGraph(graph);
//			tree.expand(1);
//			TestSuite testSuite = generator.generate(tree);			
//			
//			RequirementTracer tracer = new TransitionPairsTracerGenerator().getTracer(testSuite);
//			RequirementTracer tracer2 = new TransitionsTracerGenerator().getTracer(testSuite);
//			
//			List<List<String>> failures = new ArrayList<List<String>>();
//			
//			List<String> f1 = new ArrayList<String>();
//			f1.add("a");
//			f1.add("b");
//			f1.add("f");
//			f1.add("g");
//			f1.add("e");
//			f1.add("c");
//			List<String> f2 = new ArrayList<String>();
//			f2.add("d");
//			f2.add("e");
//			f2.add("f");
//			f2.add("g");
//			f2.add("h");
//			f2.add("i");
//			f2.add("g");
//			
//			failures.add(f1);
//			failures.add(f2);
//			
//			MapFaultAndTestSuite mapTS_Faults =  new MapFaultAndTestSuite(testSuite, failures);
//			
//			mapTS_Faults.toStringMapTCFaults();
//			
//			List<TestCase> f = mapTS_Faults.getTestCasesThatFailure();
//			for(TestCase t: f){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.println("Greedy");
//			Greedy g = new Greedy();
//			GreedyEssential ge = new GreedyEssential();
//			GreedyRedundancyEssential gre = new GreedyRedundancyEssential();
//			ReductionSimilarity sim = new ReductionSimilarity(new JaccardIndex());
//			ReductionSimilarity_Random sim_rnd = new ReductionSimilarity_Random(new JaccardIndex());
//			ReductionSimilarity_2Requirements sim_2req = new ReductionSimilarity_2Requirements(new JaccardIndex());
//			ReductionSimilarity_Random_2Requirements sim_rnd2req = new ReductionSimilarity_Random_2Requirements(new JaccardIndex());
//			ReductionSimilarity_Requeriments_2Requirements sim_rr = new ReductionSimilarity_Requeriments_2Requirements(new JaccardIndex());
//			ReductionSimilarityRequeriments sim_1rr = new ReductionSimilarityRequeriments(new JaccardIndex());
//			
//			g.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			List<TestCase> allRequirements = g.getTestSuites().getAllRequirements();
//			List<TestCase> allFailures = g.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: g.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.println("GreedyEssential");
//			
//			ge.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = ge.getTestSuites().getAllRequirements();
//			allFailures = ge.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: ge.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.println("GreedyRedundancyEssential");
//			
//			gre.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = gre.getTestSuites().getAllRequirements();
//			allFailures = gre.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: gre.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
////			System.out.println("Harrold");
////			Harrold h = new Harrold();
////			h.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
////			
////			allRequirements = h.getTestSuites().getAllRequirements();
////			allFailures = h.getTestSuites().getAllFailures();
////			System.out.print("TS all requirements: ");
////			for(TestCase t: allRequirements){
////				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
////			}
////			System.out.println();
////			
////			System.out.print("TS all failures:     ");
////			for(TestCase t: allFailures){
////				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
////			}
////			System.out.println();
//			
//			System.out.println("ReductionSimilarity");
//			
//			sim.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = sim.getTestSuites().getAllRequirements();
//			allFailures = sim.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: sim.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.println("ReductionSimilarity_Random");
//			
//			sim_rnd.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = sim_rnd.getTestSuites().getAllRequirements();
//			allFailures = sim_rnd.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: sim_rnd.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//
//			System.out.println("ReductionSimilarity_2Requirements");
//			
//			sim_2req.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = sim_2req.getTestSuites().getAllRequirements();
//			allFailures = sim_2req.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: sim_2req.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.println("ReductionSimilarity_Random_2Requirements");
//			sim_rnd2req.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = sim_rnd2req.getTestSuites().getAllRequirements();
//			allFailures = sim_rnd2req.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: sim_rnd2req.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.println("ReductionSimilarity_Requeriments_2Requirements");
//			sim_rr.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = sim_rr.getTestSuites().getAllRequirements();
//			allFailures = sim_rr.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: sim_rr.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			
//			System.out.println("ReductionSimilarityRequeriments");
//			sim_1rr.reduceTestSuite(testSuite, tracer, tracer2, mapTS_Faults);			
//			
//			allRequirements = sim_1rr.getTestSuites().getAllRequirements();
//			allFailures = sim_1rr.getTestSuites().getAllFailures();
//			System.out.print("TS all requirements: ");
//			for(TestCase t: allRequirements){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all tcs:     ");
//			for(TestCase t: sim_1rr.getTestSuites().getAllTCs_Faults()){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//			System.out.print("TS all failures:     ");
//			for(TestCase t: allFailures){
//				System.out.print(mapTS_Faults.getValueTestCase(t) + ", ");
//			}
//			System.out.println();
//		}catch (LTSBTException e){
//			e.printStackTrace();
//		} catch (FileNotFoundException e) {
//			e.printStackTrace();
//		}
//	}

}
